package blue.base.internal.core.net.aio;

import blue.base.core.net.Server;
import blue.base.core.net.ServerOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;

/**
 * @author Jin Zheng
 * @since 2021-06-28
 */
public class AioServer implements Server {
    private static Logger logger = LoggerFactory.getLogger(AioServer.class);

    private AsynchronousServerSocketChannel server;
    private AsynchronousChannelGroup group;
    private ScheduledExecutorService executorService;
    private final ServerOptions options;

    public AioServer(ServerOptions options) {
        options.check();
        this.options = options;
    }

    @Override
    public void start() {
        executorService = new ScheduledThreadPoolExecutor(options.getThread(), options.getThreadFactory());
        try {
            group = AsynchronousChannelGroup.withThreadPool(executorService);
            server = AsynchronousServerSocketChannel.open(group);
            for (var entry : options.getSocketOptionMap().entrySet()) {
                server.setOption(entry.getKey(), entry.getValue());
            }
            server.bind(new InetSocketAddress(options.getPort()));
            server.accept(null, new AioAcceptHandler(server));
            logger.info("Start aio server, port: {}", options.getPort());
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    @Override
    public void await() {
        try {
            Thread.currentThread().join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void close() {
        if (server != null) {
            try {
                server.close();
            } catch (IOException e) {
                logger.error("Error, ", e);
            }
        }
        if (group != null) {
            group.shutdown();
        }
        logger.info("Stop aio server, port: {}", options.getPort());
    }
}
